package spring.framework.beans.factory.xml;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import spring.framework.beans.BeansException;
import spring.framework.beans.PropertyValue;
import spring.framework.beans.factory.config.BeanDefinition;
import spring.framework.beans.factory.config.BeanReference;
import spring.framework.beans.factory.support.AbstractBeanDefinitionReader;
import spring.framework.beans.factory.support.BeanDefinitionRegistry;
import spring.framework.core.io.Resource;
import spring.framework.core.io.ResourceLoader;

import java.io.InputStream;

/**
 * @author pengzh
 * @class XmlBeanDefinitionReader
 * @since 2021-09-23
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinition(Resource resource) throws BeansException {
        Assert.notNull(resource, "resource can not be null");
        try (InputStream inputStream = resource.getInputStream()) {
            doLoadBeanDefinition(inputStream);
        } catch (Exception e) {
            throw new BeansException("IOException parsing xml from " + resource, e);
        }
    }


    @Override
    public void loadBeanDefinition(Resource... resources) {
        for (Resource resource : resources) {
            loadBeanDefinition(resource);
        }
    }

    @Override
    public void loadBeanDefinition(String location) {
        loadBeanDefinition(getResourceLoader().getResource(location));
    }

    protected void doLoadBeanDefinition(InputStream inputStream) throws ClassNotFoundException {
        Document document = XmlUtil.readXML(inputStream);
        Element root = document.getDocumentElement();
        NodeList childNodes = root.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {

            //解析bean
            Node node = childNodes.item(i);
            if (!(node instanceof Element)) continue;
            if (!"bean".equals(node.getNodeName())) continue;
            Element beanElement = (Element) node;

            String id = beanElement.getAttribute("id");
            String name = beanElement.getAttribute("name");
            String beanClass = beanElement.getAttribute("class");

            String beanName = StrUtil.isNotBlank(id) ? id : name;
            Class<?> clazz = Class.forName(beanClass);
            beanName = StrUtil.isNotBlank(beanName) ? beanName : clazz.getSimpleName();

            //解析bean依赖
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            for (int j = 0; j < node.getChildNodes().getLength(); j++) {
                Node childNode = node.getChildNodes().item(j);
                if (!(childNode instanceof Element)) continue;
                if (!"property".equals(childNode.getNodeName())) continue;
                Element propertyElement = (Element) childNode;

                String propertyName = propertyElement.getAttribute("name");
                String propertyValue = propertyElement.getAttribute("value");
                String ref = propertyElement.getAttribute("ref");
                Object propertyValueObj = StrUtil.isBlank(ref) ? propertyValue : new BeanReference(ref);
                PropertyValue property = new PropertyValue(propertyName, propertyValueObj);
                beanDefinition.getPropertyValues().addPropertyValue(property);
            }
            if (getRegistry().containsBeanDefition(beanName)) {
                throw new BeansException("Duplicate bean name[" + beanName + "] is not allowed");
            }

            //注册bean
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }

}
